home *** CD-ROM | disk | FTP | other *** search
JavaScript | 2000-04-19 | 18.3 KB | 452 lines | [TEXT/MOSS] |
- /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /*
- * The contents of this file are subject to the Netscape Public
- * License Version 1.1 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of
- * the License at http://www.mozilla.org/NPL/
- *
- * Software distributed under the License is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the License for the specific language governing
- * rights and limitations under the License.
- *
- * The Original Code is Mozilla Communicator client code,
- * released March 31, 1998.
- *
- * The Initial Developer of the Original Code is Netscape Communications
- * Corporation. Portions created by Netscape are
- * Copyright (C) 1998 Netscape Communications Corporation. All
- * Rights Reserved.
- *
- * Contributor(s):
- * William A. ("PowerGUI") Law <law@netscape.com>
- */
-
- /*------------------------------ nsContextMenu ---------------------------------
- | This JavaScript "class" is used to implement the browser's content-area |
- | context menu. |
- | |
- | For usage, see references to this class in navigator.xul. |
- | |
- | Currently, this code is relatively useless for any other purpose. In the |
- | longer term, this code will be restructured to make it more reusable. |
- ------------------------------------------------------------------------------*/
- function nsContextMenu( xulMenu ) {
- this.target = null;
- this.menu = null;
- this.onImage = false;
- this.onLink = false;
- this.onSaveableLink = false;
- this.link = false;
- this.inFrame = false;
- this.hasBGImage = false;
-
- // Initialize new menu.
- this.initMenu( xulMenu );
- }
-
- // Prototype for nsContextMenu "class."
- nsContextMenu.prototype = {
- // onDestroy is a no-op at this point.
- onDestroy : function () {
- },
- // Initialize context menu.
- initMenu : function ( popup, event ) {
- // Save menu.
- this.menu = popup;
-
- // Get contextual info.
- this.setTarget( document.popupNode );
-
- // Initialize (disable/remove) menu items.
- this.initItems();
- },
- initItems : function () {
- this.initOpenItems();
- this.initNavigationItems();
- this.initViewItems();
- this.initMiscItems();
- this.initSaveItems();
- this.initClipboardItems();
- },
- initOpenItems : function () {
- // Remove open/edit link if not applicable.
- this.showItem( "context-openlink", this.onSaveableLink );
- this.showItem( "context-editlink", this.onSaveableLink );
-
- // Remove open frame if not applicable.
- this.showItem( "context-openframe", this.inFrame );
-
- // Remove separator after open items if neither link nor frame.
- this.showItem( "context-sep-open", this.onSaveableLink || this.inFrame );
- },
- initNavigationItems : function () {
- // Back determined by canGoBack broadcaster.
- this.setItemAttrFromNode( "context-back", "disabled", "canGoBack" );
-
- // Forward determined by canGoForward broadcaster.
- this.setItemAttrFromNode( "context-forward", "disabled", "canGoForward" );
-
- // Reload is always OK.
-
- // Stop determined by canStop broadcaster.
- this.setItemAttrFromNode( "context-stop", "disabled", "canStop" );
- },
- initSaveItems : function () {
- // Save page is always OK.
-
- // Save frame as depends on whether we're in a frame.
- this.showItem( "context-saveframe", this.inFrame );
-
- // Save link depends on whether we're in a link.
- this.showItem( "context-savelink", this.onSaveableLink );
-
- // Save background image depends on whether there is one.
- this.showItem( "context-savebgimage", this.hasBGImage );
-
- // Save image depends on whether there is one.
- this.showItem( "context-saveimage", this.onImage );
- },
- initViewItems : function () {
- // View source is always OK.
-
- // View frame source depends on whether we're in a frame.
- this.showItem( "context-viewframesource", this.inFrame );
-
- // View Info don't work no way no how.
- this.showItem( "context-viewinfo", false );
-
- // View Frame Info isn't working, either.
- this.showItem( "context-viewframeinfo", false );
-
- // View Image depends on whether an image was clicked on.
- this.showItem( "context-viewimage", this.onImage );
- },
- initMiscItems : function () {
- // Add bookmark always OK.
-
- // Send Page not working yet.
- this.showItem( "context-sendpage", false );
- },
- initClipboardItems : function () {
- // Select All is always OK.
-
- // Copy depends on whether there is selected text.
- // Enabling this context menu item is now done through the global
- // command updating system
- // this.setItemAttr( "context-copy", "disabled", this.isNoTextSelected() );
-
- // Copy link location depends on whether we're on a link.
- this.showItem( "context-copylink", this.onLink );
-
- // Copy image location depends on whether we're on an image.
- this.showItem( "context-copyimage", this.onImage );
- },
- // Set various context menu attributes based on the state of the world.
- setTarget : function ( node ) {
- // Initialize contextual info.
- this.onImage = false;
- this.imageURL = "";
- this.onLink = false;
- this.inFrame = false;
- this.hasBGImage = false;
-
- // Remember the node that was clicked.
- this.target = node;
-
- // See if the user clicked on an image.
- if ( this.target.nodeType == 1 ) {
- if ( this.target.tagName.toUpperCase() == "IMG" ) {
- this.onImage = true;
- this.imageURL = this.target.src;
- // Look for image map.
- var mapName = this.target.getAttribute( "usemap" );
- if ( mapName ) {
- // Find map.
- var map = this.target.ownerDocument.getElementById( mapName.substr(1) );
- if ( map ) {
- // Search child <area>s for a match.
- var areas = map.childNodes;
- //XXX Client side image maps are too hard for now!
- dump( "Client side image maps not supported yet, sorry!\n" );
- areas.length = 0;
- for ( var i = 0; i < areas.length && !this.onLink; i++ ) {
- var area = areas[i];
- if ( area.nodeType == 1
- &&
- area.tagName.toUpperCase() == "AREA" ) {
- // Get type (rect/circle/polygon/default).
- var type = area.getAttribute( "type" );
- var coords = this.parseCoords( area );
- switch ( type.toUpperCase() ) {
- case "RECT":
- case "RECTANGLE":
- break;
- case "CIRC":
- case "CIRCLE":
- break;
- case "POLY":
- case "POLYGON":
- break;
- case "DEFAULT":
- // Default matches entire image.
- this.onLink = true;
- this.link = area;
- this.onSaveableLink = this.isLinkSaveable( this.link );
- break;
- }
- }
- }
- }
- }
- } else if (this.target.tagName.toUpperCase() == "INPUT") {
- if(this.target.getAttribute( "type" ).toUpperCase() == "IMAGE") {
- this.onImage = true;
- this.imageURL = this.target.src;
- }
- } else if (this.target.getAttribute( "background" )) {
- this.onImage = true;
- this.imageURL = this.target.getAttribute( "background" );
- } else {
- var cssAttr = this.target.style.getPropertyValue( "list-style-image" ) ||
- this.target.style.getPropertyValue( "list-style" ) ||
- this.target.style.getPropertyValue( "background-image" ) ||
- this.target.style.getPropertyValue( "background" );
- if ( cssAttr ) {
- this.onImage = true;
- this.imageURL = cssAttr.toLowerCase().replace(/url\("*(.+)"*\)/, "$1");
- }
- }
-
- }
-
- // See if the user clicked in a frame.
- if ( this.target.ownerDocument != window.content.document ) {
- this.inFrame = true;
- }
-
- // Bubble out, looking for link.
- var elem = this.target;
- while ( elem && !this.onLink ) {
- // Test for element types of interest.
- if ( elem.nodeType == 1 &&
- ( elem.tagName.toUpperCase() == "A"
- ||
- elem.tagName.toUpperCase() == "AREA" ) ) {
- // Clicked on a link.
- this.onLink = true;
- // Remember corresponding element.
- this.link = elem;
- // Remember if it is saveable.
- this.onSaveableLink = this.isLinkSaveable( this.link );
- }
- elem = elem.parentNode;
- }
- },
- // Returns true iff clicked on link is saveable.
- isLinkSaveable : function ( link ) {
- // Test for missing protocol property.
- if ( !link.protocol ) {
- // We must resort to testing the URL string :-(.
- dump( "Bug! Link.protocol is still undefined!\n" );
- var protocol = link.href.substr( 0, 11 );
- return protocol != "javascript:";
- } else {
- // Presume all but javascript: urls are saveable.
- return link.protocol != "javascript:";
- }
- },
- // Open linked-to URL in a new window.
- openLink : function () {
- // Determine linked-to URL.
- openNewWindowWith( this.linkURL() );
- },
- // Edit linked-to URL in a new window.
- editLink : function () {
- BrowserEditPage( this.linkURL() );
- },
- // Open clicked-in frame in its own window.
- openFrame : function () {
- openNewWindowWith( this.target.ownerDocument.location.href );
- },
- // Open new "view source" window with the frame's URL.
- viewFrameSource : function () {
- window.openDialog( "chrome://navigator/content/",
- "_blank",
- "chrome,menubar,status,dialog=no",
- this.target.ownerDocument.location.href,
- "view-source" );
- },
- viewInfo : function () {
- dump( "nsContextMenu.viewInfo not implemented yet\n" );
- },
- viewFrameInfo : function () {
- dump( "nsContextMenu.viewFrameInfo not implemented yet\n" );
- },
- // Open new window with the URL of the image.
- viewImage : function () {
- openNewWindowWith( this.imageURL );
- },
- // Save URL of clicked-on frame.
- saveFrame : function () {
- this.savePage( this.target.ownerDocument.location.href );
- },
- // Save URL of clicked-on link.
- saveLink : function () {
- this.savePage( this.linkURL() );
- },
- // Save URL of clicked-on image.
- saveImage : function () {
- this.savePage( this.imageURL );
- },
- // Save URL of background image.
- saveBGImage : function () {
- this.savePage( this.bgImageURL() );
- },
- // Generate link URL and put it on clibboard.
- copyLink : function () {
- this.copyToClipboard( this.linkURL() );
- },
- // Generate image URL and put it on the clipboard.
- copyImage : function () {
- this.copyToClipboard( this.imageURL );
- },
- // Utilities
- // Show/hide one item (specified via name or the item element itself).
- showItem : function ( itemOrId, show ) {
- var item = null;
- if ( itemOrId.constructor == String ) {
- // Argument specifies item id.
- item = document.getElementById( itemOrId );
- } else {
- // Argument is the item itself.
- item = itemOrId;
- }
- if ( item ) {
- var styleIn = item.getAttribute( "style" );
- var styleOut = styleIn;
- if ( show ) {
- // Remove style="display:none;".
- styleOut = styleOut.replace( "display:none;", "" );
-
- } else {
- // Set style="display:none;".
- if ( styleOut.indexOf( "display:none;" ) == -1 ) {
- // Add style the first time we need to.
- styleOut += "display:none;";
- }
- }
- // Only set style if it's different.
- if ( styleIn != styleOut ) {
- item.setAttribute( "style", styleOut );
- }
- }
- },
- // Set given attribute of specified context-menu item. If the
- // value is null, then it removes the attribute (which works
- // nicely for the disabled attribute).
- setItemAttr : function ( id, attr, val ) {
- var elem = document.getElementById( id );
- if ( elem ) {
- if ( val == null ) {
- // null indicates attr should be removed.
- elem.removeAttribute( attr );
- } else {
- // Set attr=val.
- elem.setAttribute( attr, val );
- }
- }
- },
- // Set context menu attribute according to like attribute of another node
- // (such as a broadcaster).
- setItemAttrFromNode : function ( item_id, attr, other_id ) {
- var elem = document.getElementById( other_id );
- if ( elem && elem.getAttribute( attr ) == "true" ) {
- this.setItemAttr( item_id, attr, "true" );
- } else {
- this.setItemAttr( item_id, attr, null );
- }
- },
- // Temporary workaround for DOM api not yet implemented by XUL nodes.
- cloneNode : function ( item ) {
- // Create another element like the one we're cloning.
- var node = document.createElement( item.tagName );
-
- // Copy attributes from argument item to the new one.
- var attrs = item.attributes;
- for ( var i = 0; i < attrs.length; i++ ) {
- var attr = attrs.item( i );
- node.setAttribute( attr.nodeName, attr.nodeValue );
- }
-
- // Voila!
- return node;
- },
- // Generate fully-qualified URL for clicked-on link.
- linkURL : function () {
- return this.link.href;
- },
- // Returns "true" if there's no text selected, null otherwise.
- isNoTextSelected : function ( event ) {
- // Not implemented so all text-selected-based options are disabled.
- return "true";
- },
- // Copy link/image url to clipboard.
- copyToClipboard : function ( text ) {
- // Get clipboard.
- var clipboard = Components
- .classes["component://netscape/widget/clipboard"]
- .getService ( Components.interfaces.nsIClipboard );
- // Create tranferable that will transfer the text.
- var transferable = Components
- .classes["component://netscape/widget/transferable"]
- .createInstance( Components.interfaces.nsITransferable );
- if ( clipboard && transferable ) {
- transferable.addDataFlavor( "text/unicode" );
- // Create wrapper for text.
- var data = createInstance( "component://netscape/supports-wstring",
- "nsISupportsWString" );
- if ( data ) {
- data.data = text ;
- transferable.setTransferData( "text/unicode", data, text.length * 2 );
- // Put on clipboard.
- clipboard.setData( transferable, null );
- }
- }
- },
- // Save specified URL in user-selected file.
- savePage : function ( url ) {
- // Default is to save current page.
- if ( !url ) {
- url = window.content.location.href;
- }
- // Use stream xfer component to prompt for destination and save.
- var xfer = Components
- .classes[ "component://netscape/appshell/component/xfer" ]
- .getService( Components.interfaces.nsIStreamTransfer );
- try {
- // When Necko lands, we need to receive the real nsIChannel and
- // do SelectFileAndTransferLocation!
-
- // Use this for now...
- xfer.SelectFileAndTransferLocationSpec( url, window );
- } catch( exception ) {
- // Failed (or cancelled), give them another chance.
- dump( "SelectFileAndTransferLocationSpec failed, rv=" + exception + "\n" );
- }
- return;
- },
- // Parse coords= attribute and return array.
- parseCoords : function ( area ) {
- return [];
- },
- toString : function () {
- return "contextMenu.target = " + this.target + "\n" +
- "contextMenu.onImage = " + this.onImage + "\n" +
- "contextMenu.onLink = " + this.onLink + "\n" +
- "contextMenu.link = " + this.link + "\n" +
- "contextMenu.inFrame = " + this.inFrame + "\n" +
- "contextMenu.hasBGImage = " + this.hasBGImage + "\n";
- }
- };
-